package cn.js189.uqc.util;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.js189.common.constants.Constants;
import cn.js189.common.domain.*;
import cn.js189.common.util.sign.DesUtils;
import cn.js189.uqc.redis.RedisOperation;
import cn.js189.uqc.service.SysEuaService;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 渠道权限相关
 */
@Slf4j
@Component
public class EuaCacheUtil {
	
	private static final String EUA_KEY = "channel:euaMap";
	
	private static final String EUA_KEY_MAP = "channel:euaKeyMap";
	
	private static final String EUA_EUC_KEY_MAP = "channel:eucEuaKeyMap";
	
	@Resource
	private SysEuaService sysEuaService;
	
	@Resource
	private RedisOperation redisOperation;
	
	@PostConstruct
	public void initStart(){
		initEua();
	}
	
	@Scheduled(cron = "0 0 0/5 * * ? ")
	public void initEua(){
		// 1、存储权限校验
		log.info("加载渠道权限数据开始......");
		this.getEuaAuthByChannelId(null);
		log.info("加载渠道权限数据结束......");
		
		// 2、加载渠道key
		log.info("加载渠道key开始......");
		this.getEuaKeyByChannelId(null);
		log.info("加载渠道key结束......");
	}
	
	public void put(String channelId) {
		if (CharSequenceUtil.isNotBlank(channelId)){
			String data = redisOperation.getForString(EUA_KEY);
			JSONObject euaMap = JSONUtil.parseObj(data);
			Object o = euaMap.get(channelId);
			if (o != null) {
				JSONObject json = new JSONObject();
				json.set(channelId, new HashSet<>());
				euaMap.set(EUA_KEY,json);
				redisOperation.setForString(EUA_KEY, JSONUtil.toJsonStr(euaMap));
			}
		}
	}
	
	public void putKey(String channelId, String key) {
		if (CharSequenceUtil.isNotBlank(channelId)){
			String value = redisOperation.getForString(EUA_KEY_MAP);
			JSONObject json = JSONUtil.parseObj(value);
			json.set(channelId + Constants.EUA_KEY_SUFFIX, key);
			redisOperation.setForString(EUA_KEY_MAP, JSONUtil.toJsonStr(json));
		}
	}
	
	public void putEncKey(String channelId, String key) {
		if (CharSequenceUtil.isNotBlank(channelId)){
			String value = redisOperation.getForString(EUA_EUC_KEY_MAP);
			JSONObject json = JSONUtil.parseObj(value);
			json.set(channelId + Constants.EUA_KEY_SUFFIX, key);
			redisOperation.setForString(EUA_EUC_KEY_MAP, JSONUtil.toJsonStr(json));
		}
	}
	
	public void put(String channelId, String actionCode) {
		if (CharSequenceUtil.isNotBlank(channelId) && CharSequenceUtil.isNotBlank(actionCode)){
			String data = redisOperation.getForString(EUA_KEY);
			JSONObject euaMap = JSONUtil.parseObj(data);
			List<EuaVo> euaList = euaMap.getBeanList(channelId,EuaVo.class);
			if (euaList == null || euaList.isEmpty()) {
				euaList = new ArrayList<>(1);
				EuaVo entity = new EuaVo();
				entity.setActionCode(actionCode.trim());
				entity.setAreaCodeList(null);
				euaList.add(entity);
				euaMap.set(channelId, euaList);
				redisOperation.setForString(EUA_KEY, euaMap.toString());
			} else {
				EuaVo entity = new EuaVo();
				entity.setActionCode(actionCode.trim());
				entity.setAreaCodeList(null);
				if (euaList.contains(entity)) {
					EuaVo entityES = getEntity(euaList, actionCode.trim());
					if (entityES != null && entityES.getAreaCodeList() != null && !entityES.getAreaCodeList().isEmpty()){
						euaList.add(entity);
						euaMap.set(channelId, euaList);
						redisOperation.setForString(EUA_KEY, euaMap.toString());
					}
				} else {
					euaList.add(entity);
					euaMap.set(channelId, euaList);
					redisOperation.setForString(EUA_KEY, euaMap.toString());
				}
			}
		}
	}
	
	public void put(String channelId, String actionCode, String areaCode) {
		if (CharSequenceUtil.isNotBlank(channelId) && CharSequenceUtil.isNotBlank(actionCode) && CharSequenceUtil.isNotBlank(areaCode)){
			String data = redisOperation.getForString(EUA_KEY);
			JSONObject euaJson = JSONUtil.parseObj(data);
			List<EuaVo> beanList = euaJson.getBeanList(channelId, EuaVo.class);
			if (beanList == null || beanList.isEmpty()) {
				beanList = new ArrayList<>(1);
				EuaVo entity = new EuaVo();
				entity.setActionCode(actionCode.trim());
				Set<String> zoneSet = new HashSet<>();
				zoneSet.add(areaCode.trim());
				entity.setAreaCodeList(zoneSet);
				beanList.add(entity);
				euaJson.set(channelId, beanList);
				redisOperation.setForString(EUA_KEY, euaJson.toString());
			} else {
				EuaVo entity = new EuaVo();
				entity.setActionCode(actionCode.trim());
				entity.setAreaCodeList(null);
				Set<String> zoneSet;
				if (beanList.contains(entity)) {
					EuaVo temp = getEntity(beanList, actionCode.trim());
					if (temp != null && temp.getAreaCodeList() == null) {
						zoneSet = new HashSet<>();
						zoneSet.add(areaCode);
						temp.setAreaCodeList(zoneSet);
					}
					beanList.add(temp);
					euaJson.set(channelId, beanList);
					redisOperation.setForString(EUA_KEY, euaJson.toString());
				} else {
					zoneSet = new HashSet<>();
					zoneSet.add(areaCode);
					entity.setAreaCodeList(zoneSet);
					beanList.add(entity);
					euaJson.set(channelId, beanList);
					redisOperation.setForString(EUA_KEY, euaJson.toString());
				}
			}
		}
	}
	
	public String check(String channelId, String actionCode, String areaCode){
		String data = redisOperation.getForString(EUA_KEY);
		JSONObject euaJson = JSONUtil.parseObj(data);
		List<EuaVo> beanList = euaJson.getBeanList(channelId, EuaVo.class);
		
		if(beanList == null || beanList.isEmpty()){
			getEuaAuthByChannelId(channelId);
			String data1 = redisOperation.getForString(EUA_KEY);
			JSONObject euaJson1 = JSONUtil.parseObj(data1);
			beanList = euaJson1.getBeanList(channelId, EuaVo.class);
		}
		
		EuaVo entity = new EuaVo();
		entity.setActionCode(actionCode);
		if (beanList == null){
			return Constants.EUA_AUTH_0001;
		}else if (actionCode == null ||
				CharSequenceUtil.isBlank(areaCode) ||
				beanList.isEmpty()){
			return Constants.EUA_AUTH_0000;
		}else if (!beanList.contains(entity)){
			return Constants.EUA_AUTH_0002;
		}
		
		EuaVo temp = getEntity(beanList, actionCode.trim());
		if (temp == null ||
				temp.getAreaCodeList() == null ||
				temp.getAreaCodeList().contains(areaCode)){
			return Constants.EUA_AUTH_0000;
		}
		return Constants.EUA_AUTH_0003;
	}
	
	private EuaVo getEntity(List<EuaVo> euaaSet, String actionCode) {
		for (EuaVo temp : euaaSet) {
			if (temp.getActionCode().equals(actionCode)) {
				return temp;
			}
		}
		return null;
	}
	
	
	/**
	 * 根据渠道id查询渠道权限
	 * @param channelId 渠道id
	 */
	public void getEuaAuthByChannelId(String channelId){
		//异步调用feign服务接口 　　
		try{
			List<EuaAuthority> authorityList = sysEuaService.listAll(channelId);
			log.info("authList:{}",authorityList);
			if(authorityList != null && !authorityList.isEmpty()){
				for (EuaAuthority euaAuthRow : authorityList) {
					if (CharSequenceUtil.isBlank(euaAuthRow.getActionCode())) {
						put(euaAuthRow.getChannelId());
					}else if(CharSequenceUtil.isBlank(euaAuthRow.getAreaCode())) {
						put(euaAuthRow.getChannelId(), euaAuthRow.getActionCode());
					}else{
						put(euaAuthRow.getChannelId(), euaAuthRow.getActionCode(),euaAuthRow.getAreaCode());
					}
				}
			}
		}catch (Exception e){
			log.error("openFeign接口调用失败：{}",e.getMessage(),e);
		}
	}
	
	/**
	 * 获取渠道key
	 * @param channelId 渠道id
	 */
	private void getEuaKeyByChannelId(String channelId){
		try{
			List<EuaChannel> authorityList = sysEuaService.listAllKey(channelId);
			if(authorityList != null && !authorityList.isEmpty()){
				for (EuaChannel channel : authorityList) {
					if (CharSequenceUtil.isNotBlank(channel.getChannelId())){
						putKey(channel.getChannelId(), channel.getChannelKey());
					}
					if ("2".equals(channel.getStatus())){
						putEncKey(channel.getChannelId(),channel.getChannelKey());
					}
				}
			}
		}catch (Exception e){
			log.error("openFeign接口调用失败：{}",e.getMessage(),e);
		}
	}
	
	
	/**
	 * 根据渠道id获取渠道key
	 * @param channelId 渠道id
	 * @return 结果
	 */
	public String getEuaKeyMapByChannelId(String channelId){
		String euaMap = redisOperation.getForString(EUA_KEY_MAP);
		JSONObject euaJson = JSONUtil.parseObj(euaMap);
		String euaKey = euaJson.getStr(channelId + Constants.EUA_KEY_SUFFIX);
		if(CharSequenceUtil.isBlank(euaKey)){
			this.getEuaKeyByChannelId(channelId);
			String euaMap1 = redisOperation.getForString(EUA_KEY_MAP);
			JSONObject euaJson1 = JSONUtil.parseObj(euaMap1);
			return euaJson1.getStr(channelId + Constants.EUA_KEY_SUFFIX);
		}else{
			return euaKey;
		}
	}
	
	
	/**
	 * 根据渠道id获取渠道key
	 * @param channelId 渠道id
	 * @return 结果
	 */
	public String getEuaEncKeyMapByChannelId(String channelId){
		
		String euaEncStr = redisOperation.getForString(EUA_EUC_KEY_MAP);
		JSONObject jsonObj = JSONUtil.parseObj(euaEncStr);
		String euaKey = jsonObj.getStr(channelId + Constants.EUA_KEY_SUFFIX);
		if(CharSequenceUtil.isBlank(euaKey)){
			this.getEuaKeyByChannelId(channelId);
			euaEncStr = redisOperation.getForString(EUA_EUC_KEY_MAP);
			jsonObj = JSONUtil.parseObj(euaEncStr);
			return jsonObj.getStr(channelId + Constants.EUA_KEY_SUFFIX);
		}else{
			return euaKey;
		}
	}
	
	public static com.alibaba.fastjson.JSONObject getEncResponse(com.alibaba.fastjson.JSONObject response, ReqHead head){
		String encInfo = getEncInfo(head);
		com.alibaba.fastjson.JSONObject returnResponse = null;
		if(encInfo!=null&& !encInfo.isEmpty()){
			response.put("body", DesUtils.getDecString(response.getString("body"),encInfo));
			returnResponse = response;
		}
		return returnResponse;
	}
	
	/**
	 * Description: <br>
	 * @param reqHead 参数信息
	 * @return String<br>
	 */
	private static String getEncInfo(ReqHead reqHead) {
		RedisOperation cache = SpringUtil.getBean(RedisOperation.class);
		String euaEncStr = cache.getForString(EUA_EUC_KEY_MAP);
		JSONObject object = JSONUtil.parseObj(euaEncStr);
		String key = object.getStr(reqHead.getChannelId()+"_key");
		if(CharSequenceUtil.isNotBlank(key)){
			return key;
		}
		return null;
	}
	
	/**
	 * 构建回参及加密
	 * @param body		 原始body,判断是否有加密key
	 * @param reqHead	 原始head部分,取channel加密
	 * @param billResult	 计费接口回参
	 * @return 结果
	 */
	public static String buildBillResponse(com.alibaba.fastjson.JSONObject body, ReqHead reqHead, String billResult) {
		com.alibaba.fastjson.JSONObject billRespObj = com.alibaba.fastjson.JSON.parseObject(billResult);
		com.alibaba.fastjson.JSONObject billBody = billRespObj.getJSONObject("body");
		if (body.containsKey(Constants.UQCENCFLAG) && null != billBody) {
			com.alibaba.fastjson.JSONObject response = new com.alibaba.fastjson.JSONObject();
			response.put("body", billBody);
			billBody = getEncResponse(response, reqHead);
			billRespObj.put("body", billBody);
		}
		
		return billRespObj.toString();
	}
	
	/**
	 * 构建bss回参及加密
	 * @param body		 原始body,判断是否有加密key
	 * @param reqHead	 原始head部分,取channel加密
	 * @param bssRes	 bss接口回参
	 * @return 结果
	 */
	public static String buildBssResponse(com.alibaba.fastjson.JSONObject body, ReqHead reqHead, String bssRes){
		JSONObject bssObj = JSONUtil.parseObj(bssRes);	// bss接口返回结果
		if (!bssObj.containsKey("head") && !bssObj.containsKey("body")){
			return JSONUtil.toJsonStr(bssObj);
		} else {
			if (body.containsKey(Constants.UQCENCFLAG)){
				com.alibaba.fastjson.JSONObject response = new com.alibaba.fastjson.JSONObject();
				response.put("body",bssObj.get("body"));
				bssObj.remove("body");
				response = getEncResponse(response,reqHead);
				bssObj.set("body", response.get("body"));
			}
		}
		return JSONUtil.toJsonStr(bssObj);
	}

	public static com.alibaba.fastjson.JSONObject transferParamForConsistence(com.alibaba.fastjson.JSONObject requestJsonParam) {

		if(requestJsonParam.containsKey("body")){
			ReqInfo reqInfo = BeanUtil.toBean(requestJsonParam.toJSONString(),ReqInfo.class);
			return  reqInfo.getBody();
		}
		return requestJsonParam;

	}
}
